<?php
namespace Framework;

class Controller
{
    public function runAction($actionID, $params=[])
    {
        if( $actionID == '' ){
            $actionID = 'index';
        }

        if (!method_exists($this, $actionID)) {
            throw new \Exception("action 未定义");
        }

        $args = $this->bindActionParams($this, $actionID, array_merge($_GET, [
            'request' => app()->request
        ]));

        return call_user_func_array([$this, $actionID], $args);
    }

    public function bindActionParams($controller, $method, $params)
    {
        $method = new \ReflectionMethod($controller, $method);
        // var_dump($method->getParameters());

        $args = [];
        foreach ($method->getParameters() as $param) {
            $name = $param->getName();
            if(array_key_exists($name, $params)){
                //参数类型校验[待完成]
                $isValid = true;
                if (PHP_VERSION_ID >= 80000) {
                    $isArray = ($type = $param->getType()) instanceof \ReflectionNamedType && $type->getName() === 'array';
                } else {
                    $isArray = $param->isArray();
                }
                if ($isArray) {
                    $params[$name] = (array)$params[$name];
                } elseif (is_array($params[$name])) {
                    $isValid = false;
                } elseif (
                    PHP_VERSION_ID >= 70000
                    && ($type = $param->getType()) !== null
                    && $type->isBuiltin()
                    && ($params[$name] !== null || !$type->allowsNull())
                ) {
                    $typeName = PHP_VERSION_ID >= 70100 ? $type->getName() : (string)$type;

                    if ($params[$name] === '' && $type->allowsNull()) {
                        if ($typeName !== 'string') { // for old string behavior compatibility
                            $params[$name] = null;
                        }
                    } else {
                        switch ($typeName) {
                            case 'int':
                                $params[$name] = filter_var($params[$name], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);
                                break;
                            case 'float':
                                $params[$name] = filter_var($params[$name], FILTER_VALIDATE_FLOAT, FILTER_NULL_ON_FAILURE);
                                break;
                            case 'bool':
                                $params[$name] = filter_var($params[$name], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
                                break;
                        }
                        if ($params[$name] === null) {
                            $isValid = false;
                        }
                    }
                }

                if (!$isValid) {
                    throw new \Exception('Invalid data received for parameter "'.$name.'".');
                }

                //
                $args[] = $params[$name];
                unset($params[$name]);

            } elseif (PHP_VERSION_ID >= 70100 && ($type = $param->getType()) !== null && !$type->isBuiltin()) {
                $typeName = $type->getName();
                if($type->allowsNull()){
                    $args[] = null;
                    throw new Exception("Unavailable service: $name");

                }else{
                    throw new Exception('Could not load required service: ' . $name);
                }

            } elseif ($param->isDefaultValueAvailable()) {
                $args[] = $actionParams[$name] = $param->getDefaultValue();

            }else{
                //action设定的参数不存在
                throw new \Exception('Could not load required service: ' . $name);
            }
        }

        return $args;
    }

    public function render($name, $params=[])
    {
        $options = [
            'debug' => true,
            'cache' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'runtime/blade',
        ];

        $blade = new \Blade\Blade($options);
        $blade->setTemplatePath(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'templates');
        return $blade->renderFile(str_replace('.', '/', $name), $params);
    }
}